001 /*
002 * Copyright 2005 Stephen J. McConnell
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.library.impl;
020
021 import java.io.File;
022 import java.io.IOException;
023 import java.net.URI;
024 import java.net.URISyntaxException;
025 import java.text.SimpleDateFormat;
026 import java.util.Arrays;
027 import java.util.ArrayList;
028 import java.util.List;
029 import java.util.Date;
030 import java.util.TimeZone;
031 import java.util.Properties;
032 import java.util.Map;
033 import java.util.Hashtable;
034
035 import net.dpml.lang.Category;
036 import net.dpml.lang.Version;
037
038 import net.dpml.library.Info;
039 import net.dpml.library.Filter;
040 import net.dpml.library.Library;
041 import net.dpml.library.Module;
042 import net.dpml.library.Resource;
043 import net.dpml.library.Type;
044 import net.dpml.library.Data;
045 import net.dpml.library.ResourceNotFoundException;
046 import net.dpml.library.info.InfoDirective;
047 import net.dpml.library.info.TypeDirective;
048 import net.dpml.library.info.ResourceDirective;
049 import net.dpml.library.info.ResourceDirective.Classifier;
050 import net.dpml.library.info.IncludeDirective;
051 import net.dpml.library.info.DependencyDirective;
052 import net.dpml.library.info.AbstractDirective;
053 import net.dpml.library.info.ValidationException;
054 import net.dpml.library.info.FilterDirective;
055 import net.dpml.library.info.Scope;
056
057 import net.dpml.transit.Artifact;
058 import net.dpml.transit.Transit;
059
060 import net.dpml.util.Resolver;
061
062
063 /**
064 * Implementation of a resource.
065 *
066 * @author <a href="http://www.dpml.net">The Digital Product Meta Library</a>
067 * @version 1.0.0
068 */
069 public class DefaultResource extends DefaultDictionary implements Resource, Resolver, Comparable
070 {
071 /**
072 * Timestamp.
073 */
074 public static final String TIMESTAMP = getTimestamp();
075
076 /**
077 * Constant SNAPSHOT symbol.
078 */
079 public static final String SNAPSHOT = "SNAPSHOT";
080
081 /**
082 * Constant RELEASE symbol.
083 */
084 public static final String RELEASE = "RELEASE";
085
086 private final DefaultLibrary m_library;
087 private final ResourceDirective m_directive;
088 private final DefaultModule m_parent;
089 private final Type[] m_types;
090 private final String[] m_typeNames;
091 private final String m_path;
092 private final File m_basedir;
093 private final Map m_filters = new Hashtable();
094 private final Data[] m_data;
095
096 /**
097 * Creation of a new default resource.
098 * @param logger the assigned logging channel
099 * @param library the reference library
100 * @param directive the directive
101 */
102 DefaultResource( final DefaultLibrary library, final AbstractDirective directive )
103 {
104 super( null, directive );
105
106 m_library = library;
107 m_directive = null;
108 m_parent = null;
109 m_types = new Type[0];
110 m_typeNames = new String[0];
111 m_path = "";
112 m_basedir = null;
113 m_data = new Data[0];
114 }
115
116 /**
117 * Creation of a new default resource.
118 * @param logger the assigned logging channel
119 * @param library the reference library
120 * @param module the parent module
121 * @param directive the resource directive
122 */
123 DefaultResource(
124 final DefaultLibrary library, final DefaultModule module, final ResourceDirective directive )
125 {
126 super( module, directive );
127 if( null == directive )
128 {
129 throw new NullPointerException( "directive" );
130 }
131
132 m_library = library;
133 m_directive = directive;
134 m_parent = module;
135
136 if( module.isRoot() )
137 {
138 m_path = directive.getName();
139 }
140 else
141 {
142 m_path = module.getResourcePath() + "/" + directive.getName();
143 }
144
145 // setup produced types
146
147 //m_types = buildTypes( directive.getTypeDirectives() );
148 m_types = directive.getTypeDirectives();
149 m_typeNames = new String[ m_types.length ];
150 for( int i=0; i<m_types.length; i++ )
151 {
152 Type type = m_types[i];
153 m_typeNames[i] = type.getID();
154 }
155
156 // setup production data
157
158 m_data = new Data[0];
159
160 // setup the resource basedir
161
162 File anchor = getAnchor();
163 String filename = m_directive.getBasedir();
164 if( null != filename )
165 {
166 String spec = resolve( filename );
167 File file = new File( spec );
168 if( file.isAbsolute() )
169 {
170 m_basedir = getCanonicalFile( file );
171 }
172 else
173 {
174 File basedir = new File( anchor, spec );
175 m_basedir = getCanonicalFile( basedir );
176 setProperty( "basedir", m_basedir.toString() );
177 }
178 }
179 else
180 {
181 if( !m_directive.getClassifier().equals( Classifier.LOCAL ) )
182 {
183 m_basedir = null;
184 }
185 else
186 {
187 final String error =
188 "Missing base directory declaration in resource ["
189 + m_path
190 + "].";
191 throw new ValidationException( error );
192 }
193 }
194
195 // setup the default properties
196
197 setProperty( "project.name", getName() );
198 if( null != m_parent )
199 {
200 setProperty( "project.group", m_parent.getResourcePath() );
201 }
202 else
203 {
204 setProperty( "project.group", "" );
205 }
206 String version = getVersion();
207 if( null != version )
208 {
209 setProperty( "project.version", getVersion() );
210 }
211
212 FilterDirective[] filters = directive.getFilterDirectives();
213 for( int i=0; i<filters.length; i++ )
214 {
215 FilterDirective filter = filters[i];
216 String token = filter.getToken();
217 m_filters.put( token, filter );
218 }
219 }
220
221 //----------------------------------------------------------------------------
222 // Resource
223 //----------------------------------------------------------------------------
224
225 /**
226 * Return a data directives.
227 * @return the associated production data
228 */
229 public Data[] getData()
230 {
231 return m_data;
232 }
233
234 /**
235 * Return the singleton library.
236 * @return the library
237 */
238 public Library getLibrary()
239 {
240 return m_library;
241 }
242
243 /**
244 * Return the name of the resource.
245 * @return the resource name
246 */
247 public String getName()
248 {
249 if( null != m_directive )
250 {
251 return m_directive.getName();
252 }
253 else
254 {
255 return null;
256 }
257 }
258
259 /**
260 * Return the resource version.
261 * @return the version
262 */
263 public String getVersion()
264 {
265 if( null == m_directive )
266 {
267 return getStandardVersion();
268 }
269 if( ResourceDirective.ANONYMOUS.equals( getClassifier() ) )
270 {
271 return m_directive.getVersion();
272 }
273 else
274 {
275 String version = getStatutoryVersion();
276 if( null != version )
277 {
278 return version;
279 }
280 else
281 {
282 return getStandardVersion();
283 }
284 }
285 }
286
287 /**
288 * Return the declard resource version.
289 * @return the statutory version
290 */
291 public String getStatutoryVersion()
292 {
293 if( null == m_directive )
294 {
295 return null;
296 }
297 else
298 {
299 String version = m_directive.getVersion();
300 if( null != version )
301 {
302 return version;
303 }
304 else
305 {
306 if( null != m_parent )
307 {
308 return m_parent.getStatutoryVersion();
309 }
310 else
311 {
312 return null;
313 }
314 }
315 }
316 }
317
318 /**
319 * Return the decimal version. If version prefixing is enabled
320 * via the <tt>project.version-prefix.enabled</tt> property then the value
321 * returned will be derived from the project major, minor and micro version
322 * properties, otherwise a null value will be returned.
323 *
324 * @return the version
325 */
326 public Version getDecimalVersion()
327 {
328 boolean flag = getBooleanProperty( "project.version-prefix.enabled", false );
329 if( flag )
330 {
331 int major = getMajorVersion();
332 int minor = getMinorVersion();
333 int micro = getMicroVersion();
334 return new Version( major, minor, micro );
335 }
336 else
337 {
338 return null;
339 }
340 }
341
342 /**
343 * Return the fully qualified path to the resource.
344 * @return the path
345 */
346 public String getResourcePath()
347 {
348 return m_path;
349 }
350
351 /**
352 * Return the basedir for this resource.
353 * @return the base directory (possibly null)
354 */
355 public File getBaseDir()
356 {
357 return m_basedir;
358 }
359
360 /**
361 * Return the resource classifier.
362 * @return the classifier (LOCAL, EXTERNAL or ANONYMOUS)
363 */
364 public Classifier getClassifier()
365 {
366 if( null != m_directive )
367 {
368 return m_directive.getClassifier();
369 }
370 else
371 {
372 return ResourceDirective.ANONYMOUS;
373 }
374 }
375
376 /**
377 * Return the info block.
378 * @return the info block
379 */
380 public Info getInfo()
381 {
382 return m_directive.getInfoDirective();
383 }
384
385 /**
386 * Return the expanded array of types associated with the resource.
387 * The returned array is a function of the types declared by a resource
388 * expanded relative to any types implied by processor dependencies.
389 * @return the type array
390 */
391 public Type[] getTypes()
392 {
393 return m_types;
394 }
395
396 /**
397 * Test if this resource is associated with a type of the supplied name.
398 * @param type the type id
399 * @return TRUE if this resource produces an artifact of the supplied type
400 */
401 public boolean isa( final String type )
402 {
403 for( int i=0; i<m_types.length; i++ )
404 {
405 Type someType = m_types[i];
406 String name = someType.getID();
407 if( name.equals( type ) )
408 {
409 return true;
410 }
411 }
412 return false;
413 }
414
415 /**
416 * Return a resource type relative to a supplied type id.
417 * @param id the type name to retrieve
418 * @return the type instance
419 * @exception IllegalArgumentException if the id value does not match
420 * a type produced by the resource.
421 */
422 public Type getType( final String id ) throws IllegalArgumentException
423 {
424 for( int i=0; i<m_types.length; i++ )
425 {
426 Type type = m_types[i];
427 if( type.getID().equals( id ) )
428 {
429 return type;
430 }
431 }
432 final String error =
433 "Type name ["
434 + id
435 + "] not recognized with the scope of resource ["
436 + getResourcePath()
437 + "].";
438 throw new IllegalArgumentException( error );
439 }
440
441 /**
442 * Construct an link artifact for the supplied type.
443 * @param id the resource type id
444 * @return the link artifact
445 */
446 public Artifact getLinkArtifact( final String id )
447 {
448 if( null == m_directive )
449 {
450 final String error =
451 "Method not supported on virtual root.";
452 throw new UnsupportedOperationException( error );
453 }
454 if( null == id )
455 {
456 throw new NullPointerException( "id" );
457 }
458 String group = getGroupName();
459 String name = getName();
460 Type type = getType( id );
461 Version version = type.getVersion();
462 if( null == version )
463 {
464 final String error =
465 "Resource does not declare production of an alias for the requested type."
466 + "\nResource: " + this
467 + "\nType: " + id;
468 throw new IllegalArgumentException( error );
469 }
470 try
471 {
472 String spec = "link:" + id;
473 if( null != group )
474 {
475 spec = spec + ":" + group + "/" + name;
476 }
477 else
478 {
479 spec = spec + ":" + name;
480 }
481 if( !Version.NULL_VERSION.equals( version ) )
482 {
483 int major = version.getMajor();
484 int minor = version.getMinor();
485 spec = spec + "#"
486 + major
487 + "."
488 + minor;
489 }
490 return Artifact.createArtifact( spec );
491 }
492 catch( Throwable e )
493 {
494 final String error =
495 "Failed to construct link artifact for resource ["
496 + getResourcePath()
497 + "].";
498 throw new RuntimeException( error, e );
499 }
500 }
501
502 /**
503 * Construct an artifact for the supplied type.
504 * @param id the resource type identifier
505 * @return the artifact
506 */
507 public Artifact getArtifact( final String id )
508 {
509 if( null == m_directive )
510 {
511 final String error =
512 "Method not supported on virtual root.";
513 throw new UnsupportedOperationException( error );
514 }
515 if( null == id )
516 {
517 throw new NullPointerException( "id" );
518 }
519
520 String group = getGroupName();
521 String name = getName();
522 String version = getVersion();
523 String scheme = m_directive.getScheme();
524
525 try
526 {
527 return Artifact.createArtifact( scheme, group, name, version, id );
528 }
529 catch( Throwable e )
530 {
531 final String error =
532 "Failed to construct artifact for resource ["
533 + getResourcePath()
534 + "].";
535 throw new RuntimeException( error, e );
536 }
537 }
538
539 /**
540 * Return the enclosing parent module.
541 * @return the enclosing module of null if this a top-level module.
542 */
543 public Module getParent()
544 {
545 return getDefaultParent();
546 }
547
548 /**
549 * Return an array of filters associated with the resource.
550 * @return the array of filters
551 */
552 public Filter[] getFilters()
553 {
554 DefaultModule module = getDefaultParent();
555 if( null != module )
556 {
557 Map map = new Hashtable();
558 Filter[] filters = module.getFilters();
559 for( int i=0; i<filters.length; i++ )
560 {
561 Filter filter = filters[i];
562 String token = filter.getToken();
563 map.put( token, filter );
564 }
565 Filter[] local = getLocalFilters();
566 for( int i=0; i<local.length; i++ )
567 {
568 Filter filter = local[i];
569 String token = filter.getToken();
570 map.put( token, filter );
571 }
572 return (Filter[]) map.values().toArray( new Filter[0] );
573 }
574 else
575 {
576 return getLocalFilters();
577 }
578 }
579
580 //----------------------------------------------------------------------------
581 // Resolver
582 //----------------------------------------------------------------------------
583
584 /**
585 * Utility function supporting resolution of uris containing 'resource' or
586 * 'alias' schemes. If the supplied uri schem is 'resource' or 'alias' the
587 * reference is resolved to a artifact type, group and name from which a
588 * resource is resolved and the uri returned. If the scheme is resource
589 * the usri of the resource is returned. If the scheme is 'alias' a
590 * linkn alias is returned. If the scheme is not 'resource' or 'alias'
591 * the argument will be evaluated as a normal transit artifact uri
592 * specification.
593 *
594 * @param ref the uri argument
595 * @return the uri value
596 * @exception URISyntaxException if an error occurs during uri creation
597 */
598 public URI toURI( final String ref ) throws URISyntaxException
599 {
600 Artifact spec = Artifact.createArtifact( ref );
601 if( spec.isRecognized() )
602 {
603 return spec.toURI();
604 }
605 else if( ref.startsWith( "resource:" ) || ref.startsWith( "alias:" ) )
606 {
607 String type = spec.getType();
608 String group = spec.getGroup();
609 String name = spec.getName();
610 String path = group + "/" + name;
611 Library library = getLibrary();
612 try
613 {
614 Resource resource = library.getResource( path );
615 if( ref.startsWith( "resource:" ) )
616 {
617 Artifact artifact = resource.getArtifact( type );
618 return artifact.toURI();
619 }
620 else
621 {
622 Artifact artifact = resource.getLinkArtifact( type );
623 return artifact.toURI();
624 }
625 }
626 catch( ResourceNotFoundException e )
627 {
628 final String error =
629 "Unresolvable resource reference: " + path;
630 IllegalArgumentException iae = new IllegalArgumentException( error );
631 iae.initCause( e );
632 throw iae;
633 }
634 }
635 else
636 {
637 return spec.toURI();
638 }
639 }
640
641 //----------------------------------------------------------------------------
642 // implementation
643 //----------------------------------------------------------------------------
644
645 Map getFilterMap()
646 {
647 return m_filters;
648 }
649
650 Filter[] getLocalFilters()
651 {
652 return (Filter[]) getFilterMap().values().toArray( new Filter[0] );
653 }
654
655 /**
656 * Return an array of resource that are providers to this resource.
657 * @param scope the operational scope
658 * @param expand if true include transitive dependencies
659 * @param sort if true the array will sorted relative to dependencies
660 * @return the resource providers
661 */
662 public Resource[] getProviders( final Scope scope, final boolean expand, final boolean sort )
663 {
664 return getDefaultProviders( scope, expand, sort );
665 }
666
667 /**
668 * Return an array of resource that are providers to this resource. If
669 * the supplied scope is BUILD the returned resource array is equivalent
670 * <src>getProviders( Scope.BUILD, .. )</src>. If the scope is RUNTIME
671 * the returned resource array includes BUILD and RUNTIME resources. If
672 * the scope is TEST the returned array includes BUILD, RUNTIME and TEST
673 * resources.
674 * @param scope the scope of aggregation to be applied to the selection
675 * @param expand if TRUE include transitive dependencies
676 * @param sort if true the array will sorted relative to dependencies
677 * @return the resource providers
678 */
679 public Resource[] getAggregatedProviders( final Scope scope, final boolean expand, final boolean sort )
680 {
681 return getAggregatedDefaultProviders( scope, expand, sort, false );
682 }
683
684 /**
685 * Return a sorted and filtered array of providers. Resources not declaring
686 * the "jar" type as a produced type are excluded from selection. The
687 * resource array will include transitive dependencies. The method is
688 * suitable for the construction of build and test phase classloaders.
689 *
690 * @param scope the aggregation scope
691 * @return the scoped resource chain
692 */
693 public Resource[] getClasspathProviders( final Scope scope )
694 {
695 DefaultResource[] result = getAggregatedDefaultProviders( scope, true, true, true );
696 List stack = new ArrayList();
697 for( int i=0; i<result.length; i++ )
698 {
699 DefaultResource resource = result[i];
700 if( resource.isa( "jar" ) )
701 {
702 stack.add( resource );
703 }
704 }
705 return (DefaultResource[]) stack.toArray( new DefaultResource[0] );
706 }
707
708 /**
709 * Return an array of runtime providers filtered relative to a supplied
710 * classloading category. Resources not declaring the "jar" type as a
711 * produced type are excluded from selection. The resource array returned
712 * from this operation is a sorted transitive sequence excluding all
713 * resource references by any category higher than the supplied category.
714 * This method is typically used to construct information suitable for
715 * the gerneration of plugin metadata.
716 *
717 * @param category the classloader category
718 * @return the category scoped resource chain
719 */
720 public Resource[] getClasspathProviders( final Category category )
721 {
722 DefaultResource[] resources = getClasspathDefaultProviders( category );
723 return sortDefaultResources( resources, Scope.RUNTIME );
724 }
725
726 /**
727 * Return an array of resources that are consumers of this resource.
728 * @param expand if true the returned array includes consumers associated
729 * through transitive dependency relationships, otherwise the array is
730 * limited to direct consumers
731 * @param sort if true the array is sorted relative to depenency relationships
732 * @return the array of consumer projects
733 */
734 public Resource[] getConsumers( final boolean expand, final boolean sort )
735 {
736 return getDefaultConsumers( expand, sort );
737 }
738
739 /**
740 * Return the underlying resource defintion.
741 * @return the resource directive
742 */
743 public ResourceDirective getResourceDirective()
744 {
745 return m_directive;
746 }
747
748 /**
749 * Return a filename using the layout strategy employed by the cache.
750 * @param id the artifact type
751 * @return the filename
752 */
753 public String getLayoutPath( final String id )
754 {
755 Artifact artifact = getArtifact( id );
756 return Transit.getInstance().getCacheLayout().resolveFilename( artifact );
757 }
758
759 /**
760 * Return a directive suitable for publication as an external description.
761 * @param module the enclosing module
762 * @return the resource directive
763 */
764 ResourceDirective exportResource( final DefaultModule module )
765 {
766 if( null == m_directive )
767 {
768 final String error =
769 "Cannot export from the root module.";
770 throw new UnsupportedOperationException( error );
771 }
772 String name = getName();
773 String version = getVersion();
774 String basedir = null;
775 InfoDirective info = m_directive.getInfoDirective();
776 TypeDirective[] types = m_directive.getTypeDirectives();
777 TypeDirective[] exportedTypes = createExportedTypes( types );
778 DependencyDirective[] dependencies = createDeps( module );
779 Properties properties = getExportProperties();
780 return ResourceDirective.createResourceDirective(
781 name, version, Classifier.EXTERNAL, basedir,
782 info, exportedTypes, dependencies, properties, null );
783 }
784
785 TypeDirective[] createExportedTypes( final TypeDirective[] types )
786 {
787 TypeDirective[] export = new TypeDirective[ types.length ];
788 for( int i=0; i<export.length; i++ )
789 {
790 TypeDirective type = types[i];
791 String id = type.getID();
792 Version version = type.getVersion();
793 export[i] = new TypeDirective( id, version );
794 }
795 return export;
796 }
797
798 private DependencyDirective[] createDeps( final DefaultModule module )
799 {
800 ArrayList list = new ArrayList();
801 createIncludeDirectives( module, list, Category.SYSTEM );
802 createIncludeDirectives( module, list, Category.PUBLIC );
803 createIncludeDirectives( module, list, Category.PROTECTED );
804 createIncludeDirectives( module, list, Category.PRIVATE );
805 if( list.size() == 0 )
806 {
807 return new DependencyDirective[0];
808 }
809 else
810 {
811 IncludeDirective[] includes =
812 (IncludeDirective[]) list.toArray( new IncludeDirective[0] );
813 DependencyDirective runtime =
814 new DependencyDirective( Scope.RUNTIME, includes );
815 return new DependencyDirective[]{runtime};
816 }
817 }
818
819 boolean isaDescendant( final DefaultModule module )
820 {
821 if( module == this )
822 {
823 return true;
824 }
825 if( m_parent == null )
826 {
827 return false;
828 }
829 else
830 {
831 if( m_parent == module )
832 {
833 return true;
834 }
835 else
836 {
837 return m_parent.isaDescendant( module );
838 }
839 }
840 }
841
842 private void createIncludeDirectives(
843 final DefaultModule module, final List list, final Category category )
844 {
845 DefaultResource[] providers =
846 getDefaultProviders( Scope.RUNTIME, true, category );
847 for( int i=0; i<providers.length; i++ )
848 {
849 DefaultResource provider = providers[i];
850 if( provider.isaDescendant( module ) )
851 {
852 // create a ref
853 String path = provider.getResourcePath();
854 IncludeDirective include =
855 new IncludeDirective(
856 IncludeDirective.REF,
857 category,
858 path,
859 null );
860 list.add( include );
861 }
862 else
863 {
864 // create a urn
865
866 Type[] types = provider.getTypes();
867 for( int j=0; j<types.length; j++ )
868 {
869 Type type = types[j];
870 String label = type.getID();
871 Artifact artifact = provider.getArtifact( label );
872 String urn = artifact.toString();
873 IncludeDirective include =
874 new IncludeDirective(
875 IncludeDirective.URI,
876 category,
877 urn,
878 null );
879 list.add( include );
880 }
881 }
882 }
883 }
884
885 //----------------------------------------------------------------------------
886 // Object
887 //----------------------------------------------------------------------------
888
889 /**
890 * Return a string representation of the resource in the form 'resource:[path]'.
891 * @return the string value
892 */
893 public String toString()
894 {
895 if( null != m_directive )
896 {
897 if( m_directive.isLocal() )
898 {
899 return toString( "project" );
900 }
901 }
902 return toString( "resource" );
903 }
904
905 String toString( final String type )
906 {
907 return type + ":" + getResourcePath() + "#" + getVersion();
908 }
909
910 /**
911 * Compare this object with another.
912 * @param other the other object
913 * @return the comparitive index
914 */
915 public int compareTo( final Object other )
916 {
917 if( other instanceof DefaultResource )
918 {
919 DefaultResource resource = (DefaultResource) other;
920 return getResourcePath().compareTo( resource.m_path );
921 }
922 else
923 {
924 return -1;
925 }
926 }
927
928 //----------------------------------------------------------------------------
929 // internals
930 //----------------------------------------------------------------------------
931
932 /**
933 * Return the singlton library.
934 * @return the library
935 */
936 DefaultLibrary getDefaultLibrary()
937 {
938 return m_library;
939 }
940
941 boolean isAnonymous()
942 {
943 if( null != m_directive )
944 {
945 return m_directive.isAnonymous();
946 }
947 return false;
948 }
949
950 boolean isLocal()
951 {
952 if( null != m_directive )
953 {
954 return m_directive.isLocal();
955 }
956 return false;
957 }
958
959 DefaultModule getDefaultParent()
960 {
961 if( null != m_parent )
962 {
963 if( m_parent.isRoot() )
964 {
965 return null;
966 }
967 }
968 return m_parent;
969 }
970
971 DefaultResource[] getAggregatedDefaultProviders(
972 final Scope scope, final boolean expanded, final boolean sort, final boolean flag )
973 {
974 DefaultResource[] resources =
975 getAggregatedDefaultProviders( scope, expanded, flag );
976 if( sort )
977 {
978 return sortDefaultResources( resources, scope );
979 }
980 else
981 {
982 Arrays.sort( resources );
983 return resources;
984 }
985 }
986
987 DefaultResource[] getAggregatedDefaultProviders(
988 final Scope scope, final boolean expanded, final boolean flag )
989 {
990 ArrayList list = new ArrayList();
991 if( !flag )
992 {
993 aggregateProviders( list, Scope.BUILD );
994 }
995 if( scope.isGreaterThan( Scope.BUILD ) )
996 {
997 aggregateProviders( list, Scope.RUNTIME );
998 }
999 if( scope.isGreaterThan( Scope.RUNTIME ) )
1000 {
1001 aggregateProviders( list, Scope.TEST );
1002 }
1003 DefaultResource[] result = (DefaultResource[]) list.toArray( new DefaultResource[0] );
1004 if( expanded )
1005 {
1006 List visited = new ArrayList();
1007 List stack = new ArrayList();
1008 for( int i=0; i<result.length; i++ )
1009 {
1010 DefaultResource resource = result[i];
1011 expandDefaultResource( visited, stack, scope, resource );
1012 }
1013 result = (DefaultResource[]) stack.toArray( new DefaultResource[0] );
1014 }
1015 return result;
1016 }
1017
1018 private void aggregateProviders( final List list, final Scope scope )
1019 {
1020 DefaultResource[] resources = getDefaultProviders( scope, false, null );
1021 for( int i=0; i<resources.length; i++ )
1022 {
1023 DefaultResource resource = resources[i];
1024 if( !list.contains( resource ) )
1025 {
1026 list.add( resource );
1027 }
1028 }
1029 }
1030
1031 DefaultResource[] getDefaultProviders(
1032 final Scope scope, final boolean expanded, final boolean sort )
1033 {
1034 DefaultResource[] resources = getDefaultProviders( scope, expanded, null );
1035 if( sort )
1036 {
1037 return sortDefaultResources( resources, scope );
1038 }
1039 else
1040 {
1041 Arrays.sort( resources );
1042 return resources;
1043 }
1044 }
1045
1046 DefaultResource[] getDefaultProviders(
1047 final Scope scope, final boolean expand, final Category category )
1048 {
1049 ArrayList visited = new ArrayList();
1050 ArrayList stack = new ArrayList();
1051 DefaultResource[] providers = getLocalDefaultProviders( scope, category );
1052 for( int i=0; i<providers.length; i++ )
1053 {
1054 DefaultResource provider = providers[i];
1055 if( expand )
1056 {
1057 expandDefaultResource( visited, stack, scope, provider );
1058 }
1059 else if( !stack.contains( provider ) )
1060 {
1061 stack.add( provider );
1062 }
1063 }
1064 return (DefaultResource[]) stack.toArray( new DefaultResource[0] );
1065 }
1066
1067 DefaultResource[] getLocalDefaultProviders( final Scope scope, final Category category )
1068 {
1069 if( null == m_directive )
1070 {
1071 return new DefaultResource[0];
1072 }
1073 IncludeDirective[] includes = getLocalIncludes( scope, category );
1074 DefaultResource[] resources = new DefaultResource[ includes.length ];
1075 for( int i=0; i<includes.length; i++ )
1076 {
1077 IncludeDirective include = includes[i];
1078 if( include.getMode().equals( IncludeDirective.URI ) )
1079 {
1080 try
1081 {
1082 String value = include.getValue();
1083 String urn = resolve( value );
1084 Properties properties = include.getProperties();
1085 resources[i] = m_library.getAnonymousResource( urn, properties );
1086 }
1087 catch( URISyntaxException e )
1088 {
1089 final String error =
1090 "Invalid uri value: " + include.getValue();
1091 throw new RuntimeException( error, e );
1092 }
1093 catch( InvalidNameException e )
1094 {
1095 final String error =
1096 "An anonomous dependency include reference to ["
1097 + include
1098 + "] within the resource ["
1099 + getResourcePath()
1100 + "] could not be resolved.";
1101 throw new InvalidNameException( error, e );
1102 }
1103 catch( Exception e )
1104 {
1105 final String error =
1106 "Unexpected error during dynamic resource creation.";
1107 throw new RuntimeException( error, e );
1108 }
1109 }
1110 else
1111 {
1112 String ref = getIncludeReference( include );
1113 try
1114 {
1115 DefaultResource resource = m_library.getDefaultResource( ref );
1116 resources[i] = resource;
1117 }
1118 catch( InvalidNameException e )
1119 {
1120 if( null == category )
1121 {
1122 final String error =
1123 "A dependency include ["
1124 + ref
1125 + "] within ["
1126 + this
1127 + "] referencing ["
1128 + ref
1129 + "] under the scope ["
1130 + scope
1131 + "] is unknown.";
1132 throw new InvalidNameException( error );
1133 }
1134 else
1135 {
1136 final String error =
1137 "A dependency include within ["
1138 + this
1139 + "] referencing ["
1140 + ref
1141 + "] under the scope ["
1142 + scope
1143 + "] and category ["
1144 + category
1145 + "] is unknown.";
1146 throw new InvalidNameException( error );
1147 }
1148 }
1149 }
1150 }
1151 return resources;
1152 }
1153
1154 private IncludeDirective[] getLocalIncludes( final Scope scope, final Category category )
1155 {
1156 DependencyDirective dependency = m_directive.getDependencyDirective( scope );
1157 if( null == category )
1158 {
1159 return dependency.getIncludeDirectives();
1160 }
1161 else
1162 {
1163 return dependency.getIncludeDirectives( category );
1164 }
1165 }
1166
1167 private void expandDefaultResource(
1168 final List visited, final List stack, final Scope scope, final DefaultResource resource )
1169 {
1170 if( visited.contains( resource ) )
1171 {
1172 return;
1173 }
1174 else
1175 {
1176 visited.add( resource );
1177 boolean flag = !scope.equals( Scope.BUILD );
1178 DefaultResource[] providers = resource.getAggregatedDefaultProviders( scope, false, flag );
1179 for( int i=0; i<providers.length; i++ )
1180 {
1181 DefaultResource provider = providers[i];
1182 expandDefaultResource( visited, stack, scope, provider );
1183 }
1184 stack.add( resource );
1185 }
1186 }
1187
1188 private String getIncludeReference( final IncludeDirective directive )
1189 {
1190 if( null == m_parent )
1191 {
1192 return directive.getValue();
1193 }
1194 else
1195 {
1196 if( IncludeDirective.REF.equals( directive.getMode() ) )
1197 {
1198 return directive.getValue();
1199 }
1200 else
1201 {
1202 String path = m_parent.getResourcePath();
1203 if( "".equals( path ) )
1204 {
1205 return directive.getValue();
1206 }
1207 else
1208 {
1209 String key = directive.getValue();
1210 return path + "/" + key;
1211 }
1212 }
1213 }
1214 }
1215
1216 //----------------------------------------------------------------------------
1217 // consumer concerns
1218 //----------------------------------------------------------------------------
1219
1220 boolean isaConsumer( final DefaultResource resource )
1221 {
1222 DefaultResource[] resources = getAggregatedDefaultProviders( Scope.TEST, false, false );
1223 for( int i=0; i<resources.length; i++ )
1224 {
1225 DefaultResource provider = resources[i];
1226 if( resource.equals( provider ) )
1227 {
1228 return true;
1229 }
1230 }
1231 return false;
1232 }
1233
1234 DefaultResource[] getDefaultConsumers( final boolean expand, final boolean sort )
1235 {
1236 DefaultResource[] consumers = getDefaultConsumers( expand );
1237 if( sort )
1238 {
1239 return sortDefaultResources( consumers, Scope.TEST );
1240 }
1241 else
1242 {
1243 return consumers;
1244 }
1245 }
1246
1247 DefaultResource[] getDefaultConsumers( final boolean expand )
1248 {
1249 if( !expand )
1250 {
1251 ArrayList list = new ArrayList();
1252 DefaultResource[] resources = m_library.selectDefaultResources( "**/*" );
1253 for( int i=0; i<resources.length; i++ )
1254 {
1255 DefaultResource resource = resources[i];
1256 if( !list.contains( resource ) && resource.isaConsumer( this ) )
1257 {
1258 list.add( resource );
1259 }
1260 }
1261 return (DefaultResource[]) list.toArray( new DefaultResource[0] );
1262 }
1263 else
1264 {
1265 ArrayList visited = new ArrayList();
1266 ArrayList stack = new ArrayList();
1267 DefaultResource[] consumers = getDefaultConsumers( false );
1268 for( int i=0; i<consumers.length; i++ )
1269 {
1270 DefaultResource consumer = consumers[i];
1271 processConsumer( visited, stack, consumer );
1272 }
1273 return (DefaultResource[]) stack.toArray( new DefaultResource[0] );
1274 }
1275 }
1276
1277 void processConsumer( final List visited, final List stack, final DefaultResource consumer )
1278 {
1279 if( visited.contains( consumer ) )
1280 {
1281 return;
1282 }
1283 visited.add( consumer );
1284 stack.add( consumer );
1285 DefaultResource[] resources = consumer.getDefaultConsumers( false, false );
1286 for( int i=0; i<resources.length; i++ )
1287 {
1288 DefaultResource resource = resources[i];
1289 processConsumer( visited, stack, resource );
1290 }
1291 }
1292
1293 //----------------------------------------------------------------------------
1294 // classpath stuff
1295 //----------------------------------------------------------------------------
1296
1297 /**
1298 * Construct an array of resources based on the RUNTIME scoped dependencies
1299 * associated with the supplied category. The implementation builds a list
1300 * of all preceeding categories as a basis for filtering the returned list ensuring
1301 * no duplicate references are returned.
1302 * @param category the runtime classloader category
1303 * @return the array of resources the define a classloader for the category
1304 */
1305 private DefaultResource[] getClasspathDefaultProviders( final Category category )
1306 {
1307 ArrayList list = new ArrayList();
1308 for( int i=0; i<category.getValue(); i++ )
1309 {
1310 Category c = Category.parse( i );
1311 DefaultResource[] collection =
1312 getDefaultProviders( Scope.RUNTIME, true, c );
1313 for( int j=0; j<collection.length; j++ )
1314 {
1315 list.add( collection[j] );
1316 }
1317 }
1318 DefaultResource[] resources =
1319 getDefaultProviders( Scope.RUNTIME, true, category );
1320 ArrayList stack = new ArrayList();
1321 for( int i=0; i<resources.length; i++ )
1322 {
1323 DefaultResource resource = resources[i];
1324 if( resource.isa( "jar" ) && !list.contains( resource ) )
1325 {
1326 stack.add( resource );
1327 }
1328 }
1329
1330 return (DefaultResource[]) stack.toArray( new DefaultResource[0] );
1331 }
1332
1333 //----------------------------------------------------------------------------
1334 // sorting relative to dependencies
1335 //----------------------------------------------------------------------------
1336
1337 DefaultResource[] sortDefaultResources( final DefaultResource[] resources )
1338 {
1339 return sortDefaultResources( resources, Scope.TEST );
1340 }
1341
1342 DefaultResource[] sortDefaultResources( final DefaultResource[] resources, final Scope scope )
1343 {
1344 ArrayList visited = new ArrayList();
1345 ArrayList stack = new ArrayList();
1346 for( int i=0; i<resources.length; i++ )
1347 {
1348 DefaultResource resource = resources[i];
1349 resource.sortDefaultResource( visited, stack, scope, resources );
1350 }
1351 return (DefaultResource[]) stack.toArray( new DefaultResource[0] );
1352 }
1353
1354 void sortDefaultResource(
1355 final List visited, final List stack, final Scope scope, final DefaultResource[] resources )
1356 {
1357 if( visited.contains( this ) )
1358 {
1359 return;
1360 }
1361 else
1362 {
1363 visited.add( this );
1364 DefaultResource[] providers =
1365 getAggregatedDefaultProviders( scope, false, false );
1366 for( int i=0; i<providers.length; i++ )
1367 {
1368 DefaultResource provider = providers[i];
1369 if( isaMember( resources, provider ) )
1370 {
1371 provider.sortDefaultResource( visited, stack, scope, resources );
1372 }
1373 }
1374 if( !stack.contains( this ) )
1375 {
1376 stack.add( this );
1377 }
1378 }
1379 }
1380
1381 boolean isaMember( final DefaultResource[] resources, final DefaultResource resource )
1382 {
1383 for( int i=0; i<resources.length; i++ )
1384 {
1385 DefaultResource r = resources[i];
1386 if( resource == r )
1387 {
1388 return true;
1389 }
1390 }
1391 return false;
1392 }
1393
1394 //----------------------------------------------------------------------------
1395 // version utilities
1396 //----------------------------------------------------------------------------
1397
1398 private String getStandardVersion()
1399 {
1400 String value = getBuildSignature();
1401 if( value.equals( SNAPSHOT ) )
1402 {
1403 return value;
1404 }
1405 else
1406 {
1407 Version decimal = getDecimalVersion();
1408 if( null != decimal )
1409 {
1410 boolean flag = getBooleanProperty( "project.version-postfix.enabled", true );
1411 if( flag )
1412 {
1413 return decimal.toString() + "-" + value;
1414 }
1415 else
1416 {
1417 return decimal.toString();
1418 }
1419 }
1420 else
1421 {
1422 return value;
1423 }
1424 }
1425 }
1426
1427 private String getBuildSignature()
1428 {
1429 String system = System.getProperty( "build.signature", null );
1430 String value = getProperty( "build.signature", system );
1431 if( null == value )
1432 {
1433 return SNAPSHOT;
1434 }
1435 else if( value.equals( "project.timestamp" ) )
1436 {
1437 return TIMESTAMP;
1438 }
1439 else
1440 {
1441 return value;
1442 }
1443 }
1444
1445 private int getMajorVersion()
1446 {
1447 return getIntegerProperty( "project.major.version", 0 );
1448 }
1449
1450 private int getMinorVersion()
1451 {
1452 return getIntegerProperty( "project.minor.version", 0 );
1453 }
1454
1455 private int getMicroVersion()
1456 {
1457 return getIntegerProperty( "project.micro.version", 0 );
1458 }
1459
1460 /**
1461 * Return the UTC YYMMDD.HHMMSSS signature of a date.
1462 * @return the UTC date-stamp signature
1463 */
1464 public static String getTimestamp()
1465 {
1466 return getTimestamp( new Date() );
1467 }
1468
1469 /**
1470 * Return the UTC YYMMDD.HHMMSSS signature of a date.
1471 * @param date the date
1472 * @return the UTC date-stamp signature
1473 */
1474 public static String getTimestamp( final Date date )
1475 {
1476 final SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
1477 sdf.setTimeZone( TimeZone.getTimeZone( "UTC" ) );
1478 return sdf.format( date );
1479 }
1480
1481 //----------------------------------------------------------------------------
1482 // other utilities
1483 //----------------------------------------------------------------------------
1484
1485 private File getAnchor()
1486 {
1487 if( null != m_parent )
1488 {
1489 File anchor = m_parent.getBaseDir();
1490 if( null != anchor )
1491 {
1492 return anchor;
1493 }
1494 }
1495 return m_library.getRootDirectory();
1496 }
1497
1498 File getCanonicalFile( final File file )
1499 {
1500 try
1501 {
1502 return file.getCanonicalFile();
1503 }
1504 catch( IOException e )
1505 {
1506 final String error =
1507 "internal error while attempting to convert the file ["
1508 + file
1509 + "] to its canonical representation.";
1510 throw new RuntimeException( error, e );
1511 }
1512 }
1513
1514 private String getGroupName()
1515 {
1516 if( m_parent.isRoot() )
1517 {
1518 return null;
1519 }
1520 else
1521 {
1522 return m_parent.getResourcePath();
1523 }
1524 }
1525 }